package com.ezops.poc.validations.validators;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.ezops.poc.validations.result.FieldValidationError;
import com.ezops.poc.validations.result.ValidationResult;
import com.ezops.poc.validations.validators.converter.ConversionException;
import com.ezops.poc.validations.validators.converter.EzopsAlpahnumericConverter;
import com.ezops.poc.validations.validators.converter.EzopsDateConverter;
import com.ezops.poc.validations.validators.converter.EzopsDecimalConverter;
import com.ezops.poc.validations.validators.converter.EzopsFixedValueConverter;
import com.ezops.poc.validations.validators.converter.EzopsNumberConverter;
import com.ezops.poc.validations.validators.converter.ITypeConverter;
import com.ezops.poc.validations.validators.converter.UnknownFormatException;

/**
 * Checks if the type of the field being under test matches the expected format
 * or not.
 * 
 * @author vgaurav
 * 
 */
@Component
@Validator(name = "Type")
public class TypeValidator extends AbstractValidator {

	private static final Logger logger = LoggerFactory
			.getLogger(TypeValidator.class);

	private Pattern pattern = Pattern.compile("\\(([^)]+)\\)");

	private String type;
	private String format;

	public TypeValidator() {

	}

	/*
	 * Mapping betweeb primitive and Wrapper class.
	 */
	private static Map<String, ITypeConverter> converterMap = new HashMap<String, ITypeConverter>();

	static {
		converterMap.put("alphanumeric", new EzopsAlpahnumericConverter());
		converterMap.put("fixed", new EzopsFixedValueConverter());
		converterMap.put("date", new EzopsDateConverter());
		converterMap.put("decimal", new EzopsDecimalConverter());
		converterMap.put("numeric", new EzopsNumberConverter());
	}

	public ValidationResult validateModel(Object model) {
		final Object value;

		try {
			final Field field = model.getClass().getDeclaredField(fieldName);
			field.setAccessible(true);
			value = field.get(model);

			ITypeConverter typeConverter = converterMap.get(type.toLowerCase());
			// Type validator is not responsible for mandatory check, so if
			// value in null then don't perform validation
			if (value == null || value.equals("")) {
				return ValidationResult.getValidResponse();
			}
			typeConverter.tryConvert((String) value, format);

			// control reaches here that means its convertible.
			return ValidationResult.getValidResponse();

		} catch (ConversionException | NoSuchFieldException | SecurityException
				| IllegalArgumentException | IllegalAccessException
				| UnknownFormatException e) {
			// TODO Auto-generated catch block
			logger.error(
					"Error Occurred while performing type validation for {} into type {} and format {}, Reason - {}",
					fieldName, type, format, e.getMessage());
		}
		// TODO
		logger.debug("Type validation failed for field name-"
				+ ezopsDisplayFieldName + " The expcted type was " + type
				+ " for " + model.toString());
		return ValidationResult.getErrorResponse(new FieldValidationError() {

			public String getFieldName() {
				return ezopsDisplayFieldName;
			}

			public String getErrorMessage() {
				return "The Data is not in required type. Expected " + type;
			}
		});
	}

	public void init(String fieldName, String... values) {
		this.ezopsDisplayFieldName = fieldName;
		String parameter = values[0];
		// Parameter can be this format Alphanumeric(0-8 chars), Numeric,
		// Date(yyyy-MM-dd),fixed(buy,sell),decimal(*.2)
		int startIndexOfFormat = parameter.indexOf("(");
		if (startIndexOfFormat == -1) {
			this.type = parameter;
			// no format defined.
			return;
		}
		this.type = parameter.substring(0, startIndexOfFormat).trim()
				.toLowerCase();
		String formatWithQuote = parameter.substring(startIndexOfFormat,
				parameter.length());
		Matcher m = pattern.matcher(formatWithQuote.trim());
		if (m.find())
			this.format = m.group(1);
		else
			throw new IllegalArgumentException(
					"The parameter passed to type validator is not in required format");

	}
}
